package ServerThreads;

import Client.Client;
import Communication.C2ServerPacket;
import Communication.S2ClientPacket;
import Communication.TaskForModule;
import Const.ClientConst;
import Const.TaskConst;
import LiftEngine.Lift;
import Server.ClientList;
import Server.ServerClientInstance;
import Server.TaskIDGetter;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.SocketException;
import java.util.LinkedList;

/**
 *
 * Class description goes here.
 *
 * @author Kamil Gajowy
 */
public class SClientCommunication extends MyThreadServer {

    private ServerClientInstance _me;
    private boolean needWaiting;
    private boolean liftChanged = false;
    private int lastFloor;
    private LinkedList<TaskForModule> tMList;
    /**
    * Default constructor.
     * @param name
     * @param cListReference
     * @param liftRef
     *//*
    public SClientCommunication(String name, ClientList cListReference, Lift liftRef){
        super(name,cListReference,liftRef);

    }*/

    SClientCommunication(String name,
            ClientList cListReference,
            Lift liftRef,
            ServerClientInstance client,
            LinkedList<TaskForModule> tList) {
        super(name,cListReference,liftRef);
        _me = client;
        needWaiting = false;
        this.tMList = tList;
    }

    @Override
    public void run(){
        try {
            System.out.println("\tforked thread for new customer...");
            lastFloor = this.getLiftReference().getCurrentFloor();
            _me.getSocket().setSoTimeout(750);
            send();
            while (serverRunning) {
                if (!liftChanged && !needWaiting) {
                    receive();
                }
                waitForLift();
                send();
                if (liftChanged) {
                    liftChanged = false;
                    needWaiting = true;
                }
                //do stuff;
            }
        } catch (SocketException ex) {
            System.out.println("@WARNING " + ex.getMessage());
        }
    }

    private void receive() {
        //System.out.print("\nreceiving...");
        try
        {
            ObjectInputStream ois =
                    new ObjectInputStream(_me.getSocket().getInputStream());
                Object readObject = ois.readObject();
                if ( readObject instanceof C2ServerPacket ){
                    //System.out.println(" done.\n");
                    clientOperate((C2ServerPacket)readObject);
                }
        } 
        catch (ClassNotFoundException ex) {}
        catch (IOException ex) {}
    }

    private void waitForLift() {
        while(needWaiting){
            try {
                checkIfLiftMoved();
                checkIfLiftIsOnMyFloor();
                SClientCommunication.sleep(100);
            }
            catch (InterruptedException ex) {}
        }
    }

    private void send() {
        ObjectOutputStream oos;
        S2ClientPacket packet;
        //System.out.print("\nsending...");
        try {
            oos = new ObjectOutputStream(_me.getSocket().getOutputStream());
            packet = new S2ClientPacket(
                    getLiftReference().getCurrentFloor(),
                    getLiftReference().getLiftDirection(),
                    _me.getClient().getState());
            oos.writeObject(packet);
            oos.flush();
            //System.out.println(" done.");
        }
        catch (IOException ex) {}
    }


    private void clientOperate(C2ServerPacket inpacket) {
        Client cl = _me.getClient();
        int desDir = inpacket.getDesiredDirection();
        int desFloor = inpacket.getDesiredFloor();
        int currFloor = inpacket.getCurrentFloor();

        if ( cl.getCurrentFloor() == ClientConst.Undefined &&
                currFloor != ClientConst.Undefined){
            cl.setCurrentFloor(currFloor);

            cl.setState(ClientConst.ClientFree);
        }

        if ( cl.getDesiredDirection() == ClientConst.Undefined &&
                desDir != ClientConst.Undefined){
            cl.setDesiredDirection(desDir);
            this.getClientList().getWaitingQueue().addPerson(_me);
            this.getClientList().getConnectedQueue().removePerson(_me);
            
            synchronized(tMList){
                TaskIDGetter.getInstance().incrementID();
                this.tMList.add(new TaskForModule(
                        TaskIDGetter.getInstance().getID(),
                        cl,TaskConst.ActionPersonJoinedWaiting));
            }
            System.out.println("X\tadd task");
            //todo delete
            //this.getLiftReference().getTask().setPersonToPackOnFloorX(cl.getCurrentFloor(), 1);

            //cl.setState(ClientConst.ClientDeciding);
            cl.setState(ClientConst.ClientWaiting);
            this.getLiftReference().updateLiftTask();
            needWaiting = true;
        }

        if ( cl.getDesiredFloor() == ClientConst.Undefined &&
                desFloor != ClientConst.Undefined){
            cl.setDesiredFloor(desFloor);
            this.getClientList().getTravelingQueue().addPerson(_me);
            this.getClientList().getWaitingQueue().removePerson(_me);
            cl.setState(ClientConst.ClientTraveling);
            needWaiting = true;
            synchronized(tMList){
                TaskIDGetter.getInstance().incrementID();
                this.tMList.add(new TaskForModule(
                        TaskIDGetter.getInstance().getID(),
                        cl,TaskConst.ActionPersonJoinedTraveling));
            }
            //this.getLiftReference().getTask().setPersonToUnloadOnFloorX(desFloor, 1);
        }
    }

    private void checkIfLiftMoved() {
        if ( this.getLiftReference().getCurrentFloor() != lastFloor
                && needWaiting){
            liftChanged = true;
            needWaiting = false;
            System.out.println("\t@@\t lift move detected!");
            lastFloor = this.getLiftReference().getCurrentFloor();
        }
    }

    private void checkIfLiftIsOnMyFloor() {
        if (this.getLiftReference().getCurrentFloor() ==
                _me.getClient().getCurrentFloor() &&
                !isInside() ){

            //TODO change in some way that client could determine if its
            //really time to get into lift???
            this.needWaiting = false;
        }

        if (this.getLiftReference().getCurrentFloor() ==
                _me.getClient().getDesiredFloor() &&
                isInside() ){

            //TODO change in some way that client could determine if its
            //really time to get out uf the lift??? ie. lift wasnt stopping
            //on this floor
            this.needWaiting = false;
        }

    }

    private boolean isInside(){
        return _me.getClient().isInLift();
    }



}//end class
